package net.atomarrow.db.parser;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.atomarrow.configs.Config;
import net.atomarrow.db.orm.IDomainControl;
import net.atomarrow.util.StringUtil;

/**
 * 条件组
 *
 * @author Michael
 *
 */
public class Conditions implements Serializable{
	List<Condition> cs = new ArrayList<Condition>();
	boolean haveOrder;
	boolean haveGroup;

	String sql;
	Expression expression;

	boolean parenthesesStart;
	boolean parenthesesDoubleStart;
	boolean sqlCache;
	private Class clazz;
	private Class returnClazz;
	private String tableName;
	private String returnClassName;
	private String selectValue;
	private String finalSelectValue;
	private String join;

	String link = null;
	/**
	 * 不等于
	 */
	public final static String UE = "!=";
	/**
	 * 大于
	 */
	public final static String GT = ">";
	/**
	 * 等于
	 */
	public final static String EW = "=";
	/**
	 * 小于
	 */
	public final static String ST = "<";
	/**
	 * OR查询
	 *
	 */
	public final static String OR = "or";
	/**
	 * 表达式
	 *
	 */
	public final static String EXP = "expresson";
	/**
	 * 两值之间
	 */
	public final static String BW = "between";
	/**
	 * 从大到小排序
	 */
	public final static String DESC = "desc";
	/**
	 * 从小到大排序
	 */
	public final static String ASC = "asc";
	/**
	 * 集合内
	 */
	public final static String IN = "in";
	/**
	 * 集合内
	 */
	public final static String NOTIN = "not in";
	/**
	 * IS查询
	 */
	public final static String IS = "is";
	/**
	 * IS not 查询
	 */
	public final static String ISNOT = "is not";
	/**
	 * 模糊查询
	 */
	public final static String LIKE = "like";
	/**
	 * 模糊查询
	 */
	public final static String NOTLIKE = "not like";
	/**
	 * 模糊查询
	 */
	public final static String LIKELEFT = "likeleft";
	/**
	 * 模糊查询
	 */
	public final static String LIKERIGHT = "likeright";

	/**
	 * 按组查询
	 */
	public final static String GROUP = "group";

	/**
	 * find_in_set
	 */
	public final static String FIND_IN_SET = "find_in_set";
	/**
	 * find_in_set() = 0
	 */
	public final static String FIND_NOT_IN_SET = "find_not_in_set";

	/**
	 * and连接
	 */
	public final static String AND = "and";

	public <T> Conditions(Class<T> clazz) {
		this.clazz = clazz;
		this.returnClazz = clazz;
	}

	public <T> Conditions(Class<? extends T> clazz, Class<T> returnClazz) {
		this.clazz = clazz;
		this.returnClazz = returnClazz;
	}

	public <T> Conditions(String tableName) {
		this.tableName=tableName;
		this.returnClassName=tableName;
	}

	public <T> Conditions(String tableName,String returnClassName) {
		this.tableName=tableName;
		this.returnClassName=returnClassName;
	}

	public void setSelectValue(String selectValue){
		this.selectValue=selectValue;
	}
	/**
	 * 括号开始
	 */
	public Conditions parenthesesStart() {
		Condition condition = new Condition();
		condition.setLink("(");
		cs.add(condition);
		return this;
	}
	/**
	 * 双括号开始
	 */
	public Conditions parenthesesDoubleStart() {
		Condition condition = new Condition();
		condition.setLink("((");
		cs.add(condition);
		return this;
	}
	/**
	 * 括号结束
	 */
	public Conditions parenthesesEnd() {
		Condition condition = new Condition();
		condition.setLink(")");
		cs.add(condition);
		return this;
	}

	/**
	 * 添加 "=" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putEW(Serializable fieldName, Serializable value){
		return put(fieldName,value,EW);
	}

	public Conditions putEWIfOk(Serializable fieldName, Serializable value) {
		return isOk(value) ? putEW(fieldName, value) : this;
	}

	public boolean isOk(Serializable value) {
		if (value == null) {
			return false;
		} else {
			Class clazz = value.getClass();
			if (clazz.equals(Boolean.class) || clazz.equals(boolean.class)) {
				return true ;
			} else if (clazz.equals(Integer.class) || clazz.equals(int.class)) {
				return (Integer)value > 0;
			} else if (clazz.equals(String.class)) {
				return StringUtil.isNotBlank(value.toString());
			} else if (clazz.equals(Double.class) || clazz.equals(double.class)) {
				return (Double)value > 0;
			} else if (clazz.equals(Long.class) || clazz.equals(long.class)) {
				return (Long)value > 0;
			} else if (clazz.equals(Short.class) || clazz.equals(short.class)) {
				return (Short)value > 0;
			} else if (clazz.equals(Byte.class) || clazz.equals(byte.class)) {
				return (Byte)value > 0;
			} else if (clazz.equals(Float.class) || clazz.equals(float.class)) {
				return (Float)value > 0;
			}
		}
		return true;
	}
	/**
	 * 添加 "!=" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putUE(Serializable fieldName, Serializable value){
		return put(fieldName,value,UE);
	}
	/**
	 * 添加 ">" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putGT(Serializable fieldName, Serializable value){
		return put(fieldName,value,GT);
	}
	public Conditions putGTIfOk(Serializable fieldName, Serializable value) {
		return isOk(value) ? putGT(fieldName, value) : this;
	}
	/**
	 * 添加 "<" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putST(Serializable fieldName, Serializable value){
		return put(fieldName,value,ST);
	}
	public Conditions putSTIfOk(Serializable fieldName, Serializable value) {
		return isOk(value) ? putST(fieldName, value) : this;
	}
	/**
	 * 添加 ">=" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putGE(Serializable fieldName, Serializable value){
		return put(fieldName,value,GT+EW);
	}
	public Conditions putGEIfOk(Serializable fieldName, Serializable value) {
		return isOk(value) ? putGE(fieldName, value) : this;
	}
	/**
	 * 添加 "<=" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putSE(Serializable fieldName, Serializable value){
		return put(fieldName,value,ST+EW);
	}
	public Conditions putSEIfOk(Serializable fieldName, Serializable value) {
		return isOk(value) ? putSE(fieldName, value) : this;
	}
	/**
	 * 添加 "or" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putOR(Serializable fieldName, Serializable value, Serializable value2){
		return put(fieldName,value,value2,OR);
	}
	/**
	 * 添加 "between" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putBW(Serializable fieldName, Serializable value, Serializable value2){
		return put(fieldName,value,value2,BW);
	}
	/**
	 * 添加 "ASC" 排序条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putASC(Serializable fieldName){
		return put(fieldName,ASC);
	}
	/**
	 * 添加 "DESC" 排序条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putDESC(Serializable fieldName){
		return put(fieldName,DESC);
	}
	/**
	 * 添加 "in" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putIS(Serializable fieldName, Serializable value){
		return put(fieldName,value,IS);
	}
	/**
	 * 添加 "in" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putISNOT(Serializable fieldName, Serializable value){
		return put(fieldName,value,ISNOT);
	}

	/**
	 * value是否在set中，
	 * @param value
	 * @param set
	 * @return
	 */
	public Conditions putFINDINSET(Serializable value, Serializable set) {
		return put(value, set, FIND_IN_SET);
	}

	public Conditions putFINDNOTINSET(Serializable value, Serializable set) {
		return put(value, set, FIND_NOT_IN_SET);
	}

	/**
	 * 添加 "in" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putIN(Serializable fieldName, Object[] value){
	    return put(fieldName,value,IN);
	}
	/**
	 * 添加 "in" 情况跨表查询条件
	 * @param fieldName 第一个表搜索需要in的值
	 * @param fieldName2 第二个表返回值
	 * @param conditions 第二个表查询条件
	 * @return
	 */
	public Conditions putIN(Serializable fieldName, Serializable fieldName2,Conditions conditions){
	    return put(fieldName,fieldName2,conditions,IN);
	}
	/**
	 * 添加 "not in" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putNOTIN(Serializable fieldName, Object[] value){
		return put(fieldName,value,NOTIN);
	}
	/**
	 * 添加 " not in" 情况跨表查询条件
	 * @param fieldName 第一个表搜索需要in的值
	 * @param fieldName2 第二个表返回值
	 * @param conditions 第二个表查询条件
	 * @return
	 */
	public Conditions putNOTIN(Serializable fieldName, Serializable fieldName2,Conditions conditions){
		return put(fieldName,fieldName2,conditions,NOTIN);
	}

	/**
	 * 添加 "like" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putLIKE(Serializable fieldName, Serializable value){
	    return put(fieldName,value,LIKE);
	}
	public Conditions putLIKEIfOK(Serializable fieldName, Serializable value){
		return isOk(value) ? put(fieldName,value,LIKE) : this;
	}
	/**
	 * 添加 "not like" 情况查询条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putNOTLIKE(Serializable fieldName, Serializable value){
		return put(fieldName,value,NOTLIKE);
	}
	/**
	 * 添加 "like" 情况查询条件,搜索给定列中左侧符合值的情况
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putLIKELEFT(Serializable fieldName, Serializable value){
		return put(fieldName,value,LIKELEFT);
	}
	public Conditions putLIKELEFTIfOk(Serializable fieldName, Serializable value){
		return isOk(value) ? put(fieldName,value,LIKELEFT) : this;
	}
	/**
	 * 添加 "like" 情况查询条件,搜索给定列中右侧符合值的情况
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putLIKERIGHT(Serializable fieldName, Serializable value){
		return put(fieldName,value,LIKERIGHT);
	}
	public Conditions putLIKERIGHTIfOk(Serializable fieldName, Serializable value){
		return isOk(value) ? put(fieldName,value,LIKERIGHT) : this;
	}
	/**
	 * 添加 "group" 分组条件
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putGROUP(Serializable fieldName){
		return put(fieldName,GROUP);
	}
	/**
	 * 添加表达式
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putEXP(Expression expression){
		return put(null,expression,EXP);
	}
	/**
	 * 添加表达式
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Conditions putEXP(String sql,Serializable ...params){
		Expression expression = new Expression(sql, params);
		return putEXP(expression);
	}
	/**
	 * 添加查询条件
	 *
	 * @param fieldName
	 *            字段名称
	 * @param value
	 *            内容值
	 * @param compareState
	 *            比较状态 直接填写"=" ">" "<" "!=" "like" "in"或者Conditions.GT Conditions.EW
	 *            Conditions.ST Conditions.UE Conditions.LIKE Conditions.IN
	 *
	 */
	public Conditions put(Serializable fieldName, Serializable value,
			String compareState) {

	    if(value==null){
	        if(compareState.equals(IS)||compareState.equals(ISNOT)){

	        }else{
	            throw new RuntimeException(fieldName+" value can not be null");
	        }
        }

		if(compareState.equals(IN)||compareState.equals(NOTIN)){

		}else if (compareState.equals(LIKE)) {
			value = "'%" + StringUtil.replace(value.toString(), "'", "''")+ "%'";
		}else if(compareState.equals(LIKELEFT)){
			value = "'" + StringUtil.replace(value.toString(), "'", "''")+ "%'";
			compareState=LIKE;
		}else if(compareState.equals(LIKERIGHT)){
			value = "'%" +StringUtil.replace(value.toString(), "'", "''") + "'";
			compareState=LIKE;
		}else if (compareState.equals(NOTLIKE)) {
            value = "'%" + StringUtil.replace(value.toString(), "'", "''")+ "%'";
            compareState=NOTLIKE;
        }else if(compareState.equals(IS)||compareState.equals(ISNOT)){
			if(value!=null){
				value=processValue(value);
			}
		}else {
			value=processValue(value);
		}
		cs.add(new Condition(fieldName, value, compareState));
		return  this;
	}

	/**
	 * 排序条件 只能做为conditons的最后一个条件，不可在其它条件之前put
	 *
	 * @param fieldName
	 *            字段名称
	 * @param compareState
	 *            "desc" "asc" "group" 或者Conditions.DESC Conditions.ASC
	 *            Conditions.GROUP
	 */
	public Conditions put(Serializable fieldName, String compareState) {
		if (compareState.equals(DESC) || compareState.equals(ASC)) {
			this.haveOrder = true;
		}else if (compareState.equals(GROUP)) {
			this.haveGroup = true;
		}else {
			new Exception("Conditions中" + fieldName
					+ "字段搜索条件错误，此处只能使用ASC、DESC、GROUP").printStackTrace();
		}
		cs.add(new Condition(fieldName, null, compareState));
		return this;
	}

	/**
	 * 区间查询条件,OR 查询条件
	 *
	 * @param fieldName
	 *            字段名称
	 * @param value1
	 *            起始值|值1
	 * @param value2
	 *            结束值|值2
	 * @param compareState
	 *            BW OR IN
	 */
	public Conditions put(Serializable fieldName, Serializable value1,
			Serializable value2, String compareState) {
       if(value1==null||value2==null){
            throw new RuntimeException(fieldName+" value can not be null");
        }
		if(compareState.equals(OR)||compareState.equals(BW)){
			value1 = processValue(value1);
			value2 = processValue(value2);
			cs.add(new Condition(fieldName, value1, value2, compareState));
		}else if(compareState.equals(IN)||compareState.equals(NOTIN)){
			cs.add(new Condition(fieldName, value1, value2, compareState));
		}
		return this;
	}

	/**
	 * 处理字符串格式
	 * @param value
	 * @return
	 */
	private Serializable processValue(Serializable value) {
		return ValueProcessor.processValue(value);
	}
//
//	/**
//	 * 处理排序放在最后group倒数第二
//	 *
//	 * @param condition
//	 * @return
//	 */
//	private void processGroupOrder() {
//		Condition condition = cs.get(cs.size() - 1);
//		// 最后一个条件是排序的情况
//		if (condition.getCompareState().equals(DESC)
//				|| condition.getCompareState().equals(ASC)) {
//			condition = cs.get(cs.size() - 2);
//			if (condition.getCompareState().equals(GROUP)) {
//				return;
//			}
//
//		} else {// 最后一个条件不是排序
//			Condition o = null;
//			Condition g = null;
//			for (int i = 0; i < cs.size(); i++) {
//				if (cs.get(i).getCompareState().equals(DESC)
//						|| cs.get(i).getCompareState().equals(ASC)) {
//					o = cs.remove(i);
//					break;
//				}
//			}
//			for (int i = 0; i < cs.size(); i++) {
//				if (cs.get(i).getCompareState().equals(GROUP)) {
//					g = cs.remove(i);
//					break;
//				}
//			}
//			cs.add(g);
//			cs.add(o);
//		}
//	}

//	/**
//	 * 处理排序放在最后
//	 *
//	 * @param condition
//	 * @return
//	 */
//	private void processOrder() {
//		Condition condition = cs.get(cs.size() - 1);
//		// 最后一个条件是排序的情况
//		if (condition.getCompareState().equals(DESC)
//				|| condition.getCompareState().equals(ASC)) {
//			return;
//		} else {// 最后一个条件不是排序
//			Condition c = null;
//			for (int i = 0; i < cs.size(); i++) {
//				if (cs.get(i).getCompareState().equals(DESC)
//						|| cs.get(i).getCompareState().equals(ASC)) {
//					c = cs.remove(i);
//					break;
//				}
//			}
//			cs.add(c);
//		}
//	}


	public Conditions setClazz(Class clazz) {
		this.clazz = clazz;
		this.returnClazz=clazz;
		return this;
	}

	public Class getConditonClass() {
		return this.clazz;
	}

	public Class getReturnClass() {
		return this.returnClazz;
	}

	/**
	 * 在不销毁的情况下创建新的实例 效果和创建新的对象相同 效率比创建新对象高
	 *
	 * @param clazz
	 */
	public Conditions newConditions(Class clazz) {
		return this.newConditions(clazz, clazz);
	}
	/**
	 * 在不销毁的情况下创建新的实例 效果和创建新的对象相同 效率比创建新对象高
	 *
	 * @param clazz
	 */
	public Conditions newConditions(Class clazz,Class returnClass) {
		clear();
		this.clazz = clazz;
		this.returnClazz = returnClass;
		tableName=clazz.getSimpleName();
		returnClassName=returnClazz.getSimpleName();
		return this;
	}
	/**
	 * 在不销毁的情况下创建新的实例 效果和创建新的对象相同 效率比创建新对象高
	 *
	 * @param clazz
	 */
	public Conditions newConditions(String tableName) {
		return this.newConditions(tableName, tableName);
	}

	/**
	 * 在不销毁的情况下创建新的实例 效果和创建新的对象相同 效率比创建新对象高
	 *
	 * @param clazz
	 */
	public Conditions newConditions(String tableName,String returnTableName) {
		clear();
		this.tableName=tableName;
		this.returnClassName=returnTableName;
		return this;
	}

	private void clear(){
		this.cs.clear();
		this.haveGroup = false;
		this.haveOrder = false;
		this.link = null;
		parenthesesStart = false;
		parenthesesDoubleStart=false;
		sql = null;
	}
	/**
	 * 添加链接关系，不添加默认值为AND
	 *
	 * @param link
	 *            使用Conditions中的静态变量如Conditions.OR
	 */
	public Conditions setLink(String link) {
		Condition condition = new Condition();
		condition.setLink(link);
		cs.add(condition);
		return this;
	}

	/**
	 * 设置连接符为or
	 * @return
	 */
	public Conditions or() {
		return setLink(Conditions.OR);
	}
	/**
	 * 得到搜索条件
	 *
	 * @return
	 */
	public Map<Serializable, Condition> getConditions() {
		Map<Serializable, Condition> maps = new HashMap<Serializable, Condition>();
		for (Condition con : cs) {
			maps.put(con.getFieldName(), con);
		}
		return maps;
	}


	public String getReturnClassName() {
		if(StringUtil.isBlank(returnClassName)){
			returnClassName=returnClazz.getSimpleName();
		}
		return returnClassName;
	}

	public void setReturnClassName(String returnClassName) {
		this.returnClassName = returnClassName;
	}

	public void setReturnClass(Class returnClazz) {
		this.returnClazz = returnClazz;
		returnClassName=returnClazz.getSimpleName();
	}

	public String getTableName() {
		if(StringUtil.isBlank(tableName)){
			IDomainControl domainControl = Config.getConfig().configDomainControl();
			if(domainControl!=null){
				tableName=domainControl.getDomainName(clazz);
			}else{
				tableName=clazz.getSimpleName();
			}
		}
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
		this.returnClassName=tableName;
	}

	public void setExpression(Expression expression) {
		this.expression=expression;
		sql=processValue(expression).toString();
	}

	public void setExpression(String sql,Serializable ...params) {
		Expression expression = new Expression(sql,params);
		setExpression(expression);
	}

	public Expression getExpression() {
		return expression;
	}

	public String getSelectValue() {
		if(selectValue==null){
			return getSelect().concat("* ");
		}
		return getSelect().concat(selectValue).concat(" ");
	}

	public String getSelect(){
		if(sqlCache){
			return "select sql_cache ";
		}
		return "select ";
	}

	public boolean getSqlCache() {
		return sqlCache;
	}

	public void setSqlCache(boolean sqlCache) {
		this.sqlCache = sqlCache;
	}

    public String getFinalSelectValue() {
        return finalSelectValue;
    }

    public void setFinalSelectValue(String finalSelectValue) {
        this.finalSelectValue = finalSelectValue;
    }

    public String getJoin() {
        return join==null?"":join;
    }

    public void setJoin(String join) {
        this.join = join;
    }

}
